# -*- coding: utf-8 -*-
from typing import Dict, List

import gevent
from zinx.ziface.irequest import IRequest
from zinx.ziface.irouter import IRouter
from zinx.ziface.imsghandler import IMsgHandler
from zinx.utils.globalobj import GlobalObject, GlobalGevents
from gevent.queue import Queue


class MsgHandler(IMsgHandler):
    """
    消息管理实现层
    """

    def __init__(self):
        self.Apis: Dict[int, IRouter] = {}  # type : dict[int, IRouter]
        self.WorkerPoolSize = GlobalObject.WorkerPoolSize       # worker数量
        self.TaskQueue: List[Queue[IRequest]] = []    # 每一个worker对应一个Queue，所有Queue用List管理

    def DoMsgHandler(self, request: IRequest):
        """
        调度/执行对应的Router消息处理方法
        :param request:
        :return:
        """
        # 判断当前request绑定的API处理方法是否已经存在
        if request.GetMsgID() not in self.Apis.keys():
            print("接口 msgId = ", request.GetMsgID(), " 未找到!")
            return

        # 执行对应处理方法
        self.Apis[request.GetMsgID()].PreHandle(request)
        self.Apis[request.GetMsgID()].Handle(request)
        self.Apis[request.GetMsgID()].PostHandle(request)

    def AddRouter(self, msgID: int, router: IRouter):
        """
        为消息添加具体的处理逻辑
        :param msgID:
        :param router:
        :return:
        """
        # 1 判断当前msg绑定的API处理方法是否已经存在
        if msgID in self.Apis.keys():
            print("接口 msgId = ", msgID, " 已存在，无法再次添加!")
            return
        # 2 添加msg与api的绑定关系
        self.Apis[msgID] = router
        print("接口 msgId = ", msgID, " 添加成功!")

    def StartWorkerPool(self):
        # 遍历需要启动worker的数量，依此启动
        for i in range(0, self.WorkerPoolSize):
            # 一个worker被启动
            # 给当前worker对应的任务队列开辟空间
            self.TaskQueue.append(Queue())
            # 启动当前Worker，阻塞的等待对应的任务队列是否有消息传递进来
            g1 = gevent.spawn(self.StartOneWorker, i)
            GlobalGevents.append(g1)

    def StartOneWorker(self, workerID: int):
        print("Worker ID = ", workerID, " 已启动。")
        # 不断的等待队列中的消息
        while True:
            # 有消息则取出队列的Request，并执行绑定的业务方法
            try:
                temp_req = self.TaskQueue[workerID].get_nowait()
                self.DoMsgHandler(temp_req)
            except:
                # get_nowait并不会主动切换其他协程，使用sleep切换到其他协程，保证不在当前协程中循环运行
                gevent.sleep(0)
                continue

    def SendMsgToTaskQueue(self, request: IRequest):
        """
        根据ConnID来分配当前的连接应该由哪个worker负责处理 轮询的平均分配法则
        :return:
        """
        # 得到需要处理此条连接的workerID
        worker_id = request.GetConnection().GetConnID() % self.WorkerPoolSize
        print("添加 ConnID=", request.GetConnection().GetConnID(), " 请求 msgID=", request.GetMsgID(), "到 workerID=",
              worker_id)
        # 将请求消息发送给任务队列
        self.TaskQueue[worker_id].put(request)


def NewMsgHandler() -> IMsgHandler:
    return MsgHandler()
